package xyz.hanks.library.bang;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;

/**
 *Created by Miroslaw Stanek on 20.12.2015.
 */
public class DotsView extends Component implements Component.DrawTask, Component.EstimateSizeListener,
    Component.BindStateChangedListener {
  private static final int DOTS_COUNT = 7;
  private static final int OUTER_DOTS_POSITION_ANGLE = 51;

  private int COLOR_1 = 0xFFFFC107;
  private int COLOR_2 = 0xFFFF9800;
  private int COLOR_3 = 0xFFFF5722;
  private int COLOR_4 = 0xFFF44336;

  private int width = 0;
  private int height = 0;

  private final Paint[] circlePaints = new Paint[4];

  private int centerX;
  private int centerY;

  private float maxOuterDotsRadius;
  private float maxInnerDotsRadius;
  private float maxDotSize;

  private float currentProgress = 0;

  private float currentRadius1 = 0;
  private float currentDotSize1 = 0;

  private float currentDotSize2 = 0;
  private float currentRadius2 = 0;


  public DotsView(Context context) {
    super(context);
    init();
  }

  public DotsView(Context context, AttrSet attrs) {
    super(context, attrs);
    init();
  }

  public DotsView(Context context, AttrSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
  }


  private void init() {
    addDrawTask(this);
    setEstimateSizeListener(this);
    setBindStateChangedListener(this);
    for (int i = 0; i < circlePaints.length; i++) {
      circlePaints[i] = new Paint();
      circlePaints[i].setStyle(Paint.Style.FILL_STYLE);
      circlePaints[i].setAntiAlias(true);
    }
  }


  @Override
  public void onDraw(Component component, Canvas canvas) {
    drawOuterDotsFrame(canvas);
    drawInnerDotsFrame(canvas);
  }

  private void drawOuterDotsFrame(Canvas canvas) {
    for (int i = 0; i < DOTS_COUNT; i++) {
      int cX = (int) (centerX + currentRadius1 * Math.cos(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
      int cY = (int) (centerY + currentRadius1 * Math.sin(i * OUTER_DOTS_POSITION_ANGLE * Math.PI / 180));
      canvas.drawCircle(cX, cY, currentDotSize1, circlePaints[i % circlePaints.length]);
    }
  }

  private void drawInnerDotsFrame(Canvas canvas) {
    for (int i = 0; i < DOTS_COUNT; i++) {
      int cX = (int) (centerX + currentRadius2 * Math.cos((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
      int cY = (int) (centerY + currentRadius2 * Math.sin((i * OUTER_DOTS_POSITION_ANGLE - 10) * Math.PI / 180));
      canvas.drawCircle(cX, cY, currentDotSize2, circlePaints[(i + 1) % circlePaints.length]);
    }
  }

  public void setCurrentProgress(float currentProgress) {
    this.currentProgress = currentProgress;

    updateInnerDotsPosition();
    updateOuterDotsPosition();
    updateDotsPaints();
    updateDotsAlpha();

    invalidate();
  }

  public float getCurrentProgress() {
    return currentProgress;
  }

  private void updateInnerDotsPosition() {
    if (currentProgress < 0.3f) {
      this.currentRadius2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0, 0.3f,
          0.f, maxInnerDotsRadius);
    } else {
      this.currentRadius2 = maxInnerDotsRadius;
    }
    if (currentProgress == 0) {
      this.currentDotSize2 = 0;
    } else if (currentProgress < 0.2) {
      this.currentDotSize2 = maxDotSize;
    } else if (currentProgress < 0.5) {
      this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.2f, 0.5f,
          maxDotSize, 0.3 * maxDotSize);
    } else {
      this.currentDotSize2 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f,
          maxDotSize * 0.3f, 0);
    }

  }

  private void updateOuterDotsPosition() {
    if (currentProgress < 0.3f) {
      this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.0f, 0.3f,
          0, maxOuterDotsRadius * 0.8f);
    } else {
      this.currentRadius1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.3f, 1f,
          0.8f * maxOuterDotsRadius, maxOuterDotsRadius);
    }
    if (currentProgress == 0) {
      this.currentDotSize1 = 0;
    } else if (currentProgress < 0.7) {
      this.currentDotSize1 = maxDotSize;
    } else {
      this.currentDotSize1 = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.7f, 1f,
          maxDotSize, 0);
    }
  }

  private void updateDotsPaints() {
    if (currentProgress < 0.5f) {
      float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0f, 0.5f, 0,
          1f);
      circlePaints[1].setColor(new Color(COLOR_2));
      circlePaints[0].setColor(new Color(COLOR_1));
      circlePaints[2].setColor(new Color(COLOR_3));
      circlePaints[3].setColor(new Color(COLOR_4));
    } else {
      float progress = (float) Utils.mapValueFromRangeToRange(currentProgress, 0.5f, 1f, 0,
          1f);
      circlePaints[0].setColor(new Color(COLOR_2));
      circlePaints[1].setColor(new Color(COLOR_3));
      circlePaints[2].setColor(new Color(COLOR_4));
      circlePaints[3].setColor(new Color(COLOR_1));
    }
  }

  public void setColors(int primaryColor, int secondaryColor) {
    COLOR_1 = primaryColor;
    COLOR_2 = secondaryColor;
    COLOR_3 = primaryColor;
    COLOR_4 = secondaryColor;
    invalidate();
  }

  private void updateDotsAlpha() {
    float progress = (float) Utils.clamp(currentProgress, 0.6f, 1f);
    int alpha = (int) Utils.mapValueFromRangeToRange(progress, 0.6f, 1f, 255, 0);
    circlePaints[0].setAlpha(alpha);
    circlePaints[1].setAlpha(alpha);
    circlePaints[2].setAlpha(alpha);
    circlePaints[3].setAlpha(alpha);
  }

  public void setSize(int width, int height) {
    this.width = width;
    this.height = height;
    invalidate();
  }

  @Override
  public boolean onEstimateSize(int i, int i1) {
    int wMode = EstimateSpec.getMode(i);
    int hMode = EstimateSpec.getMode(i1);
    if (width != 0 && height != 0) {
      setEstimatedSize(Component.EstimateSpec.getSizeWithMode(width, wMode),
          Component.EstimateSpec.getSizeWithMode(height, hMode));
      return true;
    }
    return false;
  }

  @Override
  public void onComponentBoundToWindow(Component component) {
    centerX = getWidth() / 2;
    centerY = getHeight() / 2;
    maxDotSize = 5;
    maxOuterDotsRadius = getWidth() / 2 - maxDotSize * 2;
    maxInnerDotsRadius = 0.8f * maxOuterDotsRadius;
  }

  @Override
  public void onComponentUnboundFromWindow(Component component) {

  }
}